﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Device.Location;
using System.Windows;
using System.Windows.Data;
using Ninject;
using SydEnergiPrototype.DataAccess;
using SydEnergiPrototype.Logic;
using SydEnergiPrototype.Model;
using System.Linq;

namespace SydEnergiPrototype.ViewModel
{
    public class FindCabinetViewModel : ViewModelBase, IFindCabinetViewModel, IObserver
    {
        private const int MaxDistance = 1000;

        #region Members

        private Cabinets _cabinetCollection;
        public Cabinets CabinetCollection
        {
            get 
            {
                if (_cabinetCollection == null)
                {
                    InitializeCollections();
                }
                return _cabinetCollection;
            }
            set { _cabinetCollection = value; Notify("CabinetCollection");}
        }

        private CollectionViewSource _cabinetFilter;
        public CollectionViewSource CabinetFilter
        {
            get
            {
                if (_cabinetFilter == null)
                    _cabinetFilter = new CollectionViewSource();
                
                return _cabinetFilter;
            }
        }

        private CollectionViewSource _entryFilter;
        public CollectionViewSource EntryFilter
        {
            get
            {
                if(_entryFilter == null)
                    _entryFilter = new CollectionViewSource();

                return _entryFilter;
            }
        }

        private List<IEntry> _entryCollection;
        public List<IEntry> EntryCollection
        {
            get
            {
                if (_entryCollection == null)
                {
                    InitializeCollections();
                }
                return _entryCollection;
            }
            set { _entryCollection = value; Notify("EntryCollection"); }
        }

        private RelayCommand _searchCommand;
        public RelayCommand SearchCommand
        {
            get
            {
                if(_searchCommand == null)
                    _searchCommand = new RelayCommand(param => Search());

                return _searchCommand;
            }
        }

        private RelayCommand _refreshCommand;
        public RelayCommand RefreshCommand
        {
            get
            {
                if(_refreshCommand == null)
                    _refreshCommand = new RelayCommand(param => InitializeCollections());

                return _refreshCommand;
            }
        }

        private SaveEntryToDataStoreCommand _saveEntryCommand;
        public SaveEntryToDataStoreCommand SaveEntryCommand
        {
            get
            {
                if(_saveEntryCommand == null)
                    _saveEntryCommand = new SaveEntryToDataStoreCommand();

                return _saveEntryCommand;
            }
        }

        private SaveCabinetToDataStoreCommand _saveCabinetCommand;
        public SaveCabinetToDataStoreCommand SaveCabinetCommand
        {
            get
            {
                if(_saveCabinetCommand == null)
                    _saveCabinetCommand = new SaveCabinetToDataStoreCommand();

                return _saveCabinetCommand;
            }
        }
        
        private GeoCoordinate _position;
        public GeoCoordinate Position
        {
            get
            {
                if (_position == null)
                {
                    _position = _gpsService.CurrentPosition;
                }
                return _position;
            }
            private set { _position = value; Notify("Position"); }
        }

        private string _searchParameter;
        public string SearchParameter
        {
            get { return _searchParameter; }
            set { _searchParameter = value; Notify("SearchParameter"); }
        }

        #endregion

        private readonly IGpsService _gpsService;
        private readonly IDataService _dataService;
        private readonly ISearchQueryBuilder _searchQueryBuilder;

        [Inject]
        public FindCabinetViewModel(IGpsService gpsService, IDataService dataService, ISearchQueryBuilder searchQueryBuilder)
        {
            if(gpsService == null)
                throw new ArgumentNullException("gpsService");

            _gpsService = gpsService;
            _gpsService.PositionChanged += new EventHandler(PositionChangedHandler);

            if(dataService == null)
                throw new ArgumentNullException("dataService");
            _dataService = dataService;
            
            if(searchQueryBuilder == null)
                throw new ArgumentNullException("searchQueryBuilder");
            _searchQueryBuilder = searchQueryBuilder;

            Subject.RegisterObserver(this);

            InitializeCollections();
        }

        private void InitializeCollections()
        {
            _cabinetCollection = new Cabinets();
            _cabinetFilter = new CollectionViewSource();
            _cabinetFilter.Filter += UpdateCabinetFilter;

            _entryFilter = new CollectionViewSource();

            _dataService.RetrieveCabinets(new ReturnResult(RetrieveCabinetsCompleted));
        }


        private void RetrieveCabinetsCompleted(ReturnResult result)
        {
            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {
                CabinetCollection = result.Result;
                _cabinetFilter.Source = _cabinetCollection;

                _entryCollection = _cabinetCollection.SelectMany(cabinet => cabinet.EntryCollection).ToList();
                _entryFilter.Source = _entryCollection;
            });

        }

        private void PositionChangedHandler(object sender, EventArgs e)
        {
            Position = _gpsService.CurrentPosition;
        }

        private void UpdateCabinetFilter(object sender, FilterEventArgs e)
        {
            var c = e.Item as Cabinet;
            //e.Accepted = true;
            e.Accepted = Position.GetDistanceTo(c.CabinetPosition) < MaxDistance;
        }

        private void Search()
        {
            if(SearchParameter != null)
            {

                EntryFilter.View.Filter = e =>
                                              {
                                                  var query = _searchQueryBuilder.Build(SearchParameter);
                                                  if (e == null) return true;

                                                  var entry = e as Entry;

                                                  bool accepted = (query.StreetName != null
                                                                       ? entry.EntryAddress.StreetName.ToLower().StartsWith(query.StreetName)
                                                                       : true) &&
                                                                  (query.City != null
                                                                       ? entry.EntryAddress.City.ToLower().StartsWith(query.City)
                                                                       : true) &&
                                                                  (query.ZipCode != 0
                                                                       ? entry.EntryAddress.ZipCode == query.ZipCode
                                                                       : true);
                                                  return accepted;
                                              };
            }
        }

        public void Update()
        {
            _dataService.RetrieveCabinets(new ReturnResult(RetrieveCabinetsCompleted));
        }
    }
}
